﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using DiscoveryLogic.Analytics.Modules.Diagrams.Renders;

namespace DiscoveryLogic.Analytics.Modules.Diagrams.GravityDiagrams
{
    public class Spring
    {
        #region props
        private ISpringRenderer _Renderer;
        /// <summary></summary>
        public ISpringRenderer Renderer
        {
            get
            {
                return this._Renderer;
            }
            set
            {
                this._Renderer = value;
            }
        }

        private Particle _From;
        /// <summary></summary>
        public Particle From
        {
            get
            {
                return this._From;
            }
            set
            {
                this._From = value;
            }
        }

        private Particle _To;
        /// <summary></summary>
        public Particle To
        {
            get
            {
                return this._To;
            }
            set
            {
                this._To = value;
            }
        }

        private double _RestLength;
        /// <summary></summary>
        public double RestLength
        {
            get
            {
                return this._RestLength;
            }
            set
            {
                this._RestLength = value;
            }
        }

        private double _SpringConst;
        /// <summary></summary>
        public double SpringConst
        {
            get
            {
                return this._SpringConst;
            }
            set
            {
                this._SpringConst = value;
            }
        }

        private double _DampingConstant;
        /// <summary></summary>
        public double DampingConstant
        {
            get
            {
                return this._DampingConstant;
            }
            set
            {
                this._DampingConstant = value;
            }
        }

        private Pen _SpringPen;
        /// <summary></summary>
        public Pen SpringPen
        {
            get
            {
                return this._SpringPen;
            }
            set
            {
                this._SpringPen = value;
            }
        }
        #endregion

        #region ctor
        public Spring(Particle from, Particle to, double restLen,
            double springConst, double dampingConst)
        {
            this._From = from;
            this._To = to;
            this._RestLength = restLen;
            this._SpringConst = springConst;
            this._DampingConstant = dampingConst;
            this._SpringPen = new Pen(Brushes.White, 4.0f);
            this._Renderer = null;
        }
        #endregion

        #region pub methods
        public void Apply()
        {
            Vector deltaX = From.Position - To.Position;
            Vector deltaV = From.Velocity - To.Velocity;

            double term1 = this._SpringConst * (deltaX.Length - RestLength);
            double term2 = DampingConstant * Vector.AngleBetween(deltaV, deltaX) / Math.Max(1, deltaX.Length);

            double leftMultiplicant = -(term1 + term2);
            Vector force = deltaX;

            // FIXME: Should do something about zero-length springs here as the
            // simulation will brake on zero-length springs...
            force *= 1.0f / Math.Max(1, deltaX.Length);
            force *= leftMultiplicant;

            From.Force += force;
            To.Force -= force;
        }

        public void Render(DrawingContext dc)
        {
            if (this._Renderer != null)
                this._Renderer.Render(this, dc);
        }
        #endregion
    }
}
